package model;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;

import model.PicassaException;
import model.Syntax;
import model.semantics.BinaryOperation;
import model.semantics.ColorToken;
import model.semantics.InfinaryOperation;
import model.semantics.Operation;
import model.semantics.UnaryOperation;
import util.reflection.Reflection;
import util.resources.*;

public class Parser
{
	private ResourceManager myResources;

	// TODO: Need to update process methods to return boolean value 
	// 		 indicating whether function executed successfully
	// need to add comments
	
    public Parser ()
    {
        myResources = ResourceManager.getInstance();
        myResources.addResourcesFromFile("syntax");
    }
    
    protected Operation makeExpression (String expression)
    {
    	String modifiedExpression = preprocess(expression);
    	
    	Stack<Operation> operands = new Stack<Operation>();
    	Stack<Operation> operators = new Stack<Operation>();
    	
    	processTokens( tokenize(modifiedExpression), operands, operators);
    	
    	return buildExpressionTree(operands, operators);
    }

    protected Operation buildExpressionTree(Stack<Operation> operands, Stack<Operation> operators) 
    {
		// TODO Auto-generated method stub
    	
    	
		return null;
	}

	protected void processTokens(String[] tokenized, Stack<Operation> operands, Stack<Operation> operators) 
	{	
        for (int i = 0; i < tokenized.length; i++) 
        {
        	switch(Syntax.processToken(tokenized[i]))
        	{
        		case Syntax.LEFT_PARENTHESES: 	;//operators.push( new PunctuationToken(token));
        		case Syntax.RIGHT_PARENTHESES: 	processPunctuation(operators, operands, false);
        		case Syntax.COMMA: 				processPunctuation(operators, operands, true);
        		case Syntax.OPERATOR: 			processOperator(tokenized[i], operators, operands);
        		case Syntax.INFINARY: 			processInfinary(tokenized, i, operators, operands);
        		case Syntax.COLOR:				processColor( tokenized, i, operators, operands);
        		case Syntax.OPERAND: 			;//operands.push(new ColorToken());
        	}// end swtich
        }//end for
	}


	// TODO: need to make more expandable( maybe w/o boolean isComma)
	private void processPunctuation(Stack<Operation> operators, Stack<Operation> operands, boolean isComma) 
	{
		while (!operators.empty() && Syntax.isLeftParen(operators.peek().toString()) )
        {
			operands.add(operators.pop()); 
        }
		
		if(!isComma)
			operators.pop(); 
	} 
	
	private void processOperator(String token, Stack<Operation> operators, Stack<Operation> operands) 
	{
		while (!operators.empty() && Syntax.isOperator(operators.peek().toString()) )
		{
		    if ( ! compareShuntingOperators(token, operators.peek().toString()) )
		    	break;
		    
		    operands.add(operators.pop());
		}
		
		// TODO: operators.push(create new instance of token using reflection);
	}
	
	private boolean compareShuntingOperators(String op1, String op2)
	{
		boolean shuntingCase1 = Syntax.isAssociative(op1, Syntax.LEFT_ASSOCIATIVE) 
											&& Syntax.comparePrecedence(op1, op2) <= 0;
		
		boolean shuntingCase2 = Syntax.isAssociative(op1, Syntax.RIGHT_ASSOCIATIVE) 
											&& Syntax.comparePrecedence(op1, op2) < 0;
		
		return shuntingCase1 || shuntingCase2; 
	}

	
	private void processInfinary(String[] tokenized, int index, 
			Stack<Operation> operators, Stack<Operation> operands) 
	{
		// TODO Auto-generated method stub
	
		
		
	}
	
	private void processColor(String[] tokenized, int index,
			Stack<Operation> operators, Stack<Operation> operands)
	{
		if( (tokenized.length - index - 1) < ColorToken.getNumArguments() + 2)
			PicassaException.showError(PicassaException.ARGUMENTS_ERROR + " - insufficient for color"); //TODO: Add exception str
		
		Queue<Double> rgbValues = new LinkedList<Double>();
		for(int i = index + 1; i < tokenized.length; i += 2)
		{	
			try
			{ rgbValues.add( Double.parseDouble(tokenized[i]) ); }
			catch(Exception e) 
			{ PicassaException.showError(e.getMessage()); }
			
			if( Syntax.processToken(tokenized[i + 1]) != Syntax.COMMA )
				PicassaException.showError(PicassaException.SYNTAX_ERROR + " - missing comma in color");
		}
		
		if(rgbValues.size() == ColorToken.getNumArguments())
			operands.push( new ColorToken(rgbValues.poll(), rgbValues.poll(), rgbValues.poll()) );
		else
			PicassaException.showError(""); // TODO: THROW EXCEPTION
	}

	protected String[] tokenize(String modifiedExpression) 
	{
		return modifiedExpression.split("\\s+");
	}

	protected String preprocess(String expression) 
	{
		// TODO: need to preprocess string
		// ex. - add spaces
		
		return expression;
	}


	/**
	 * @return
	 */
	private Set<String> resourceBundleSet() 
	{
		Set<String> myWords = new HashSet<String>();
		Iterator<String> iterate = myResources.iterator();
		while(iterate.hasNext()) myWords.add(iterate.next());
		return myWords;
	}
	
	


}
